
package de.uni.wuppertal.server.ftp;

import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import de.uni.wuppertal.server.ErrorLogEntry;
import de.uni.wuppertal.server.ServerInfo;
import de.uni.wuppertal.server.ui.MainWindow;

/**
 * @author Martin Schröder
 * @author Patrik Nordsiek
 */
public class FTPServer implements ServerInfo
{
	public static final String CRLF = "\r\n";
	
	private String name;
	private int port;
	private String rootDirectory;
	
	private ServerSocket socket;
	
	private boolean terminate = false;
	
	private List<FTPServerControlConnection> connections = new ArrayList<FTPServerControlConnection>();
	
	private static HashMap<Integer, String> statusMessages;
	
	private MainWindow window;
	
	static
	{
		statusMessages = new HashMap<Integer, String>();
		
		statusMessages.put(200, "Command OK.");
		statusMessages.put(220, "Service ready for new user.");
		statusMessages.put(221, "Service closing control connection.");
		statusMessages.put(230, "User logged in, proceed.");
		
		statusMessages.put(331, "User name okay, need password.");
		statusMessages.put(332, "Need account for login.");
		
		statusMessages.put(451, "Requested action aborted. Local error in processing.");
		
		statusMessages.put(500, "Syntax error, command unrecognized.");
		statusMessages.put(501, "Syntax error in parameters or arguments.");
		statusMessages.put(502, "Command not implemented.");
		statusMessages.put(503, "Bad sequence of commands.");
		statusMessages.put(530, "Not logged in.");
	}
	
	public FTPServer()
	{
		window = MainWindow.getInstance();
	}
	
	public String getStatusMessage(int code)
	{
		if(!statusMessages.containsKey(code))
		{
			return String.valueOf(code);
		}
		
		return code + " " + statusMessages.get(code);
	}
	
	public boolean isTerminated()
	{
		return terminate;
	}
	
	public synchronized void setTerminated(boolean terminated)
	{
		this.terminate = terminated;
	}
	
	public InetAddress getAddress() throws Exception
	{
		return InetAddress.getLocalHost();
	}
	
	@Override
	public void run()
	{
		try
		{
			terminate = false;
			
			socket = new ServerSocket(port);
			socket.setSoTimeout(100);
			
			window.incrementActiveServerInstance();
			
			while(true)
			{
				try
				{
					Socket clientSocket = socket.accept();
					
					FTPServerControlConnection conn = new FTPServerControlConnection(this, clientSocket);
					conn.setDaemon(true);
					
					connections.add(conn);
					
					conn.start();
				}
				catch(SocketTimeoutException e)
				{
					if(terminate)
					{
						for(FTPServerControlConnection conn : connections)
						{
							if(conn.isAlive())
							{
								conn.setTerminate(true);
							}
						}
						
						break;
					}
				}
			}
		}
		catch(Throwable e)
		{
			window.log(new ErrorLogEntry(e));
			
			e.printStackTrace();
		}
		finally
		{
			window.decrementActiveServerInstance();
			
			try
			{
				socket.close();
			}
			catch(Throwable e)
			{
				window.log(new ErrorLogEntry(e));
				
				e.printStackTrace();
			}
			finally
			{
				socket = null;
			}
		}
	}
	
	@Override
	public String getName()
	{
		return name;
	}
	
	@Override
	public String getRootDirectory()
	{
		return rootDirectory;
	}
	
	@Override
	public synchronized void setName(String name)
	{
		this.name = name;
	}
	
	@Override
	public int getPort()
	{
		return port;
	}
	
	@Override
	public synchronized void setPort(int port)
	{
		this.port = port;
	}
	
	@Override
	public synchronized void setRootDirectory(String rootDirectory)
	{
		this.rootDirectory = rootDirectory;
	}
	
	@Override
	public synchronized void stopServer()
	{
		terminate = true;
	}
	
	@Override
	public int compareTo(ServerInfo info)
	{
		return name.compareToIgnoreCase(info.getName());
	}
}
